package com.gitee.luosl.cola.match.cases;

import com.gitee.luosl.cola.lang.Tuple2;
import com.gitee.luosl.cola.lang.Tuple3;
import com.gitee.luosl.cola.lang.Tuple4;
import com.gitee.luosl.cola.lang.function.checked.*;
import com.gitee.luosl.cola.match.exception.MatchWrapException;
import com.gitee.luosl.cola.match.guard.Guard;
import com.gitee.luosl.cola.match.pattern.Pattern;
import com.gitee.luosl.cola.match.pattern.Pattern2;
import com.gitee.luosl.cola.match.pattern.Pattern3;
import com.gitee.luosl.cola.match.pattern.Pattern4;

/**
 * Created by luosl on 2021/03/29/15:13
 */
public class CaseTry<T, U, R> extends AbstractCase<T, U, R> {

    private final CheckedFunction<? super U, ? extends R> mapper;

    public CaseTry(Pattern<? super T, ? extends U> pattern, Guard<U> guard, CheckedFunction<? super U, ? extends R> mapper) {
        super(pattern, guard);
        this.mapper = mapper;
    }

    public CaseTry(Pattern<? super T, ? extends U> pattern, CheckedFunction<? super U, ? extends R> mapper) {
        this(pattern, null, mapper);
    }

    @Override
    public R apply(U value) {
        try {
            return mapper.apply(value);
        }catch (Throwable throwable){
            throw new MatchWrapException(throwable);
        }
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, CheckedFunction<U, R> function){
        return new CaseTry<>(pattern, function);
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, CheckedFunction<U, R> function){
        return new CaseTry<>(pattern, guard, function);
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, CheckedSupplier<R> supplier){
        return new CaseTry<>(pattern, ignore -> supplier.get());
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, CheckedSupplier<R> supplier){
        return new CaseTry<>(pattern, guard, ignore -> supplier.get());
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, R value){
        return new CaseTry<>(pattern, ignore -> value);
    }

    public static <T, U, R> CaseTry<T, U, R> of(Pattern<T, U> pattern, Guard<U> guard, R value){
        return new CaseTry<>(pattern, guard, ignore -> value);
    }

    public static <T, T1, T2, R> CaseTry<T, Tuple2<T1, T2>, R> of(Pattern2<T, T1, T2> pattern, CheckedBiFunction<T1, T2, R> function){
        return new CaseTry<>(pattern, t -> function.apply(t._1, t._2));
    }

    public static <T, T1, T2, R> CaseTry<T, Tuple2<T1, T2>, R> of(Pattern2<T, T1, T2> pattern, Guard<Tuple2<T1, T2>> guard, CheckedBiFunction<T1, T2, R> function){
        return new CaseTry<>(pattern, guard, t -> function.apply(t._1, t._2));
    }

    public static <T, T1, T2, T3, R> CaseTry<T, Tuple3<T1, T2, T3>, R> of(Pattern3<T, T1, T2, T3> pattern, CheckedFunction3<T1, T2, T3, R> function){
        return new CaseTry<>(pattern, t -> function.apply(t._1, t._2, t._3));
    }

    public static <T, T1, T2, T3, R> CaseTry<T, Tuple3<T1, T2, T3>, R> of(Pattern3<T, T1, T2, T3> pattern, Guard<Tuple3<T1, T2, T3>> guard, CheckedFunction3<T1, T2, T3, R> function){
        return new CaseTry<>(pattern, guard, t -> function.apply(t._1, t._2, t._3));
    }

    public static <T, T1, T2, T3, T4, R> CaseTry<T, Tuple4<T1, T2, T3, T4>, R> of(Pattern4<T, T1, T2, T3, T4> pattern, CheckedFunction4<T1, T2, T3, T4, R> function){
        return new CaseTry<>(pattern, t -> function.apply(t._1, t._2, t._3, t._4));
    }

    public static <T, T1, T2, T3, T4, R> CaseTry<T, Tuple4<T1, T2, T3, T4>, R> of(Pattern4<T, T1, T2, T3, T4> pattern, Guard<Tuple4<T1, T2, T3, T4>> guard, CheckedFunction4<T1, T2, T3, T4, R> function){
        return new CaseTry<>(pattern, guard, t -> function.apply(t._1, t._2, t._3, t._4));
    }

    public static <T, U> CaseTry<T, U, Void> run(Pattern<T, U> pattern, CheckedRunnable runnable){
        return of(pattern, ignore -> {runnable.run(); return null;});
    }

    public static <T, U> CaseTry<T, U, Void> run(Pattern<T, U> pattern, Guard<U> guard, CheckedRunnable runnable){
        return of(pattern, guard, ignore -> {runnable.run(); return null;});
    }

    public static <T, U> CaseTry<T, U, Void> run(Pattern<T, U> pattern, CheckedConsumer<U> consumer){
        return of(pattern, data -> {consumer.accept(data); return null;});
    }

    public static <T, U> CaseTry<T, U, Void> run(Pattern<T, U> pattern, Guard<U> guard, CheckedConsumer<U> consumer){
        return of(pattern, guard, data -> {consumer.accept(data); return null;});
    }

    public static <T, T1, T2> CaseTry<T, Tuple2<T1, T2>, Void> run(Pattern2<T, T1, T2> pattern, CheckedBiConsumer<T1, T2> consumer){
        return new CaseTry<>(pattern, t -> {consumer.accept(t._1, t._2); return null;});
    }

    public static <T, T1, T2> CaseTry<T, Tuple2<T1, T2>, Void> run(Pattern2<T, T1, T2> pattern, Guard<Tuple2<T1, T2>> guard, CheckedBiConsumer<T1, T2> consumer){
        return new CaseTry<>(pattern, t -> {consumer.accept(t._1, t._2); return null;});
    }

    public static <T, T1, T2, T3> CaseTry<T, Tuple3<T1, T2, T3>, Void> run(Pattern3<T, T1, T2, T3> pattern, CheckedConsumer3<T1, T2, T3> consumer){
        return new CaseTry<>(pattern, t -> {consumer.accept(t._1, t._2, t._3); return null;});
    }

    public static <T, T1, T2, T3> CaseTry<T, Tuple3<T1, T2, T3>, Void> run(Pattern3<T, T1, T2, T3> pattern, Guard<Tuple3<T1, T2, T3>> guard, CheckedConsumer3<T1, T2, T3> consumer){
        return new CaseTry<>(pattern, guard, t -> {consumer.accept(t._1, t._2, t._3); return null;});
    }

    public static <T, T1, T2, T3, T4> CaseTry<T, Tuple4<T1, T2, T3, T4>, Void> run(Pattern4<T, T1, T2, T3, T4> pattern, CheckedConsumer4<T1, T2, T3, T4> consumer){
        return new CaseTry<>(pattern, t -> {consumer.accept(t._1, t._2, t._3, t._4); return null;});
    }

    public static <T, T1, T2, T3, T4> CaseTry<T, Tuple4<T1, T2, T3, T4>, Void> run(Pattern4<T, T1, T2, T3, T4> pattern, Guard<Tuple4<T1, T2, T3, T4>> guard, CheckedConsumer4<T1, T2, T3, T4> consumer){
        return new CaseTry<>(pattern, guard, t -> {consumer.accept(t._1, t._2, t._3, t._4); return null;});
    }
}
